package network2;

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.rmi.ServerError;
import java.util.Scanner;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class TcpEchoServer {
    private ServerSocket serverSocket = null;

    public TcpEchoServer(int port) throws IOException {
        serverSocket = new ServerSocket(port);
    }

    public void start() throws IOException {
        System.out.println("服务器启动: ");
        //此处使用线程池：CachedThreadPool，使用FixedThreadPool不太合适（线程数不应该是有固定的....）
        ExecutorService threadPool = Executors.newCachedThreadPool();
        while (true) {
            //使用这个clientSocket 和具体的客户端进行交流
            Socket clientSocket = serverSocket.accept();
            //此处使用多线程来处理
            //多线程版本
//            Thread t = new Thread(()-> {
//                processConnection(clientSocket);
//            });
//            /*ServerSocket专属于服务器使用的socket，如果是构造一个对象，需要绑定一个端口号的
//            * 然后accept方法就相当于来电话了接听电话的动作一样，此时accept一下，此时就会获得一
//            * 个socket对象，此时和客户端就遥相呼应，然后进行通信*/
//            t.start();

            //使用线程池
            threadPool.submit(()-> {
                processConnection(clientSocket);
            });
        }

    }
    //2.根据请求计算响应，但是又不需要完全就是完全的计算响应
    //使用这个方法来处理一个连接
    ///一个连接涉及到一个客户端，但是这里可能涉及到多次交互
    private void processConnection(Socket clientSocket) {
        System.out.printf("[%s : %d] 客户端上线！\n", clientSocket.getInetAddress().toString(),
                clientSocket.getPort());//得到客户端的IP和端口
        //基于上述的socket对象和客户端进行通信
        try(InputStream inputStream = clientSocket.getInputStream();
            OutputStream outputStream = clientSocket.getOutputStream()) {
            /*inputStream和outputStream就相当于是电话的听筒和麦克风，一个用来在接收数据，一个
            * 用来发送数据*/

            //由于要处理多个请求和响应，也是使用循环来进行处理
            while (true) {
                //1.读取请求
                Scanner scan = new Scanner(inputStream);
                if (!scan.hasNext()) {
                    //没有下一个数据了，此时说明读完了。（客户端关闭了连接）
                    System.out.printf("[%s : %d] 客户端下线！\n", clientSocket.getInetAddress().toString(),
                            clientSocket.getPort());
                    break;
                }
                //此处要注意：此时用next 是一直读取到换行符，空格符，其他空白符就结束，但最终返回的结果不包含书上空白符
                String request = scan.next();
                //2.根据请求计算响应
                String response = process(request);
                //3.返回响应结果
                //OutputStream 没有write String这样的功能，可以吧String中的字节数组拿出来然后进行写入
                //也可以用字符流转换一下
                PrintWriter printWriter = new PrintWriter(outputStream);
                //使用一个println来进行写入，让结果中带有一个换行\n.方便对端来来接收解析
                printWriter.println(response);
                //flush用来刷新缓冲区，保证当前写入的数据确实是发送出去了
                printWriter.flush();
                //打印日志
                System.out.printf("[%s : %d req: %s; resp: %s \n", clientSocket.getInetAddress().toString(),
                        clientSocket.getPort(), request, response);
            }
        }catch (IOException e) {
            e.printStackTrace();
        }

    }

    public String process(String request) {
        return request;
    }

    public static void main(String[] args) throws IOException {
        TcpEchoServer tcpEchoServer = new TcpEchoServer(9090);
        tcpEchoServer.start();
    }
}
/*在tcp有连接的场景下，针对连接这个概念，有两种典型的表现形式：
* 1.短连接：客户端每次给服务器发消息，先建立连接，发送请求，读取响应，关闭连接，下次再发送请求，则重新建立连接
* 2.长连接：客户端建立连接之后，连接不着急断开，然后再发送请求，读取响应，再发送请求，读取响应，若干轮之后，客户端
* 确实短时间之内不再需要使用这个连接了，此时再断开*/
/*短连接，还是得用多线程，连接建立了，请求什么时候发是不确定的，还是会产生阻塞的，还是会导致processConnection
没有立即返回的 */

